package components;


import java.util.List;

import com.uqbar.vainilla.DeltaState;
import com.uqbar.vainilla.GameComponent;
import com.uqbar.vainilla.colissions.CollisionDetector;
import components.scenes.CollisionablesScene;

public abstract class CollisionableComponent<SceneType extends CollisionablesScene, CType extends CollisionableComponent> 
	extends GameComponent<SceneType> {
	
	private CollisionDetector detector;
	private int diameter;
	private int height;
	private boolean enable;
	
	public int getDiameter() {
		return diameter;
	}

	public void setDiameter(int diameter) {
		this.diameter = diameter;
	}

	public int getHeight() {
		return height;
	}

	public void setHeight(int height) {
		this.height = height;
	}

	
	public CollisionableComponent(int aDiameter, int aHeight, double x, double y){
		this.diameter = aDiameter;
		this.height = aHeight;
		this.setX(x);
		this.setY(y);
		this.enable = true;
		this.setDetector(CollisionDetector.INSTANCE);
	}
	

	public abstract boolean isSolid();

	public CollisionDetector getDetector() {
		return detector;
	}

	public void setDetector(CollisionDetector detector) {
		this.detector = detector;
	}
	
	public void updateCoords(double diffX, double diffY) {
		this.getSceneTableComps().move(this, this.getX(), this.getY(), this.getX() + diffX, this.getY() + diffY);
		this.move(diffX, diffY);
	}
	public void relocate(double x, double y){
		this.getSceneTableComps().move(this, this.getX(), this.getY(), x, y);
		this.setX(x);
		this.setY(y);
	}
	public ComponentsHashTable getSceneTableComps() {
		return this.getScene().getComponentsTable();
	}
	
	public boolean collide(double nx, double ny){
		List<CType> comps = this.getSceneTableComps().getComponentsNearSectorExcluding(this, nx, ny, this.diameter, this.height);
		for (CType gc : comps){
				if (this.collisionNature(gc)){
					this.defineCollide(gc);
					if (gc.isSolid()){
						this.onCollisionWithSolid(gc);
						return true;
					}
				}
			}
		return false;
	}

	protected abstract void onCollisionWithSolid(CType gc);
		

	protected abstract void defineCollide(CType gc);


	public void update(DeltaState deltaState) {
		super.update(deltaState);
		if (deltaState.getDelta() < 0.1){
			this.effectiveUpdate(deltaState);
		}
	}
	
	protected void generciMove(DeltaState deltaState, double speed, double xdir, double ydir){
		double intensity = deltaState.getDelta() * speed;
		double nx, ny;
		nx = this.getX() + intensity*xdir;
		ny = this.getY() + intensity*ydir;
		
		this.collide(nx, ny);
		
		this.updateCoords(intensity*xdir, intensity*ydir);
	}

	public boolean nonSolidMove (DeltaState deltaState, double speed, double xdir, double ydir, double xdif, double ydif){
		double intensity = deltaState.getDelta() * speed;
		double nx, ny;
		nx = this.getX() + intensity*xdir + xdif;
		ny = this.getY() + intensity*ydir + ydif;
		
		if (!this.collide(nx, ny)){
			this.updateCoords(intensity*xdir, intensity*ydir);
			return true;
		}
		else {
			return false;
		}
		
	}
	protected abstract void effectiveUpdate(DeltaState deltaState);
	
	public abstract boolean collideWithRect(CType rect);
	public abstract boolean collideWithCircle(CType circle);
	public abstract boolean collisionNature(CType target);
	
	public boolean rectRectCollide(CType rec1, CType rec2){
		return this.getDetector().collidesRectAgainstRect(rec1.getX(), rec1.getY(), 
				rec1.getDiameter(), rec1.getHeight(), rec2.getX(), 
				rec2.getY(), rec2.getDiameter(), rec2.getHeight());
	}
	public boolean rectCircleCollide(CType circle, CType rect){
		return this.getDetector().collidesCircleAgainstRect(circle.getX(), 
				circle.getY(), circle.getDiameter()/2, rect.getX(), 
				rect.getY(), rect.getDiameter(), rect.getHeight());
	}
	public boolean circleCircleCollide(CType circle){
		return this.getDetector().collidesCircleAgainstCircle(this.getX(), this.getY(), 
				this.getDiameter()/2, circle.getX(), circle.getY(), circle.getDiameter()/2);
	}
		
	public void destroyComponent(){
		this.getSceneTableComps().remove(this, this.getX(), this.getY());
		this.destroy();
	}

	public boolean isEnable() {
		return enable;
	}

	public void setEnable(boolean enable) {
		this.enable = enable;
	}
	

	
}
